/******************************************************************************
Copyright:
2009 Jens Collin (jens.collin@dinomin.com)

This file is part of Envox Decompiler.

Envox Decompiler is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Envox Decompiler is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with KitchenOS.  If not, see <http://www.gnu.org/licenses/>.

******************************************************************************/
using System;
using System.Collections;

namespace EnvoxDecompiler
{
	/// <summary>
	/// Summary description for Block_Base.
	/// </summary>
	public abstract class Block
	{

		/// <summary>
		/// Types of blocks. These must not directly match the ones in 
		/// Envox (Envox changed naming schemes some times...)
		/// Make sure when adding a block that it adds itself with correct
		/// XML when generating code.
		/// </summary>
 		public enum TYPE
		{
			Start = 1,
			Stop = 4,
			EventCheck = 5,
			EventProcess = 6,
			Delay = 13,
			SetVariable = 20,
			TimeSwitch = 27,		/* TODO: Not completed */
			DBOpen = 30,
			DBSelect = 31,
			DBBrowse = 32,
			DBClose = 33,
			DBSQL = 34,
			DBTransBegin = 35,
			DBTransEnd = 36,
			FileProperties = 41,
			FindFiles = 44,		/* TODO: Not completed */
			ChangeScript = 48,
			Registry = 49,
			TcpIpListen = 66,
			TcpIpConnect = 67,
			TcpIpSend = 68,
			TcpIpReceive = 69,
			TcpIpClose = 70,
			Counter = 71,
			FindFilesNext = 72, /* Pair block with FindFiles TODO: Not completed */
			RunHostedScript = 80,
			FileRead = 85,		/* TODO: Not completed */
            SplitVariable = 95,
			Shutdown = 105,
			WaitCall = 132,		/* TODO: Not completed */
			XMLCreateDocument = 165,
			XMLSaveDocument = 167,
			XMLCreateNode = 168,
			XMLModifyNode = 171,
			XMLSetAttributes = 175,
			XMLCloseHandle = 176,
			FileReadNext = 215 /* Pair block with FileRead TODO: Not completed */
		}

		/// <summary>
		/// Types of outputs from blocks. (Next, No Response etc)
		/// These MUST match the names in Envox so check before adding!
		/// In some cases there are duplicates! This is solved by adding a "_" and an sequence id.
		/// So, EOF and EOF_2 is essentially the same.
		/// In some cases the VALUE are also the same. To overcome this constants
		/// that have same values has been added a 0x1000 constant. Since these
		/// never occur in the real .seq file, we need to have Changer-methods in subclasses.
		/// 
		/// </summary>
		public enum OUTPUT
		{
			Disconnect = 0x01,
			OpBreak = 0x02,
			CreditOut = 0x03,
			DTMF = 0x468,
			Exit = 0x04,
			ModeChange = 0x05,
			Restart = 0x06,
			DiskFull = 0x07,
			UserLevel = 0x08,
			DDEReceived = 0x09,
			FaxSendTone = 0x0A,
			FaxRecvTone = 0x0B,
			ModemTone = 0x0C,
			Notify = 0x0D,
			Ring = 0x0E,
			HookFlash = 0x0F,
			ConfDestroyed = 0x10,
			IEReceived = 0x11,
			UUIReceived = 0x12,
			DummyOutput = 0x3C, /* This one is not connected ever */
			DummyOutput_2 = 0x4e, /* Neither is this one */
			DummyOutput_1 = 0x5b, /* Neither is this one */
			Next = 0x64,
			Working = 0x1064,					/* In timeSwitch this is the Next output 0x1000 */
			NoResponse = 0x65,
			RecordsetEmpty = 0x69,
			EOF_2 = 0x6A,						/* Found in DBBrowse... 'EOF' seems to be non-unique. */
			BOF = 0x6B,
			Closed = 0x73,
			DayBreak = 0x74,
			Weekend = 0x075,
			InvalidVarType = 0x3E9,
			Record = 0x3EA,
			RecParams = 0x3EB,
			PlayError = 0x3EC,
			ReleaseCall = 0x3ED,
			WaitCall = 0x3EE,
			ExprVal = 0x3EF,
			SetVariable = 0x3F0,
			Compare = 0x3F1,
			ConnectFailed = 0x3F2,
			DbLocked = 0x3F3,
			DbAuthorize = 0x3F4,
			DbTimeout = 0x3F5,
			DbFieldType = 0x3FD,
			CallError = 0x401,
			SetQueue = 0x402,
			ReadQueue = 0x403,
			DbSQLExpr = 0x404,
			InvalidParam = 0x405,
			NotFound_1 = 0x406,
			Create = 0x407,
			Delete = 0x408,
			Rename = 0x409,
			Copy = 0x40A,
			Timeout = 0x413,
			NoDLLFile = 0x415,
			NoDLLFunc = 0x416,
			NoMemory = 0x417,
			BadInput = 0x418,
			BadOutput = 0x419,
			Move = 0x420,
			FuncFailed = 0x41A,
			CantSetVariable = 0x41B,
			Match = 0x41C,
			NoFile = 0x41D,
			Append = 0x421,
			Execute = 0x422,
			ZeroLen = 0x425,
			NoAuthorization = 0x426,
			NotFound_3 = 0x427,
			Failed = 0x42B,
			NotFound = 0x42C,
			Timeout_1 = 0x43D,
			Failed_3 = 0x43E,
			NoResources = 0x43F,
			Overflow = 0x442,
			NotFound_2 = 0x444,
			Exist = 0x449,
			Failed_2 = 0x44F,					/* Found in FileRead... 'Failed' seems to be non-unique. */
			AccessDenied = 0x448,
			InvalidPosition = 0x454,
			CantReadFile = 0x455,
			CantCreateEngine = 0x456,
			ParseError = 0x457,
			RunFailed = 0x458,
			EOF = 0x459,
            TooLong = 0x45A,
            TooShort = 0x45C,
			InvalidHandle = 0x461,
			DbFailed = 0x466,
			SQLFailed = 0x467
		}


		/// <summary>
		/// List of pairs of string and error id for blocks.
		/// DbFailed is 38 in DBOpen, but 35 in DBClose etc.
		/// Each blocks constructor sets this up and we scan on write.
		/// </summary>
		protected ArrayList outputErrorIds = new ArrayList();

		// Name of the block
		protected string _name = "";
		// What should be displayed in console window
		protected string _channelDisplayText = "";
		// Id number of this block
		protected long _blockId;
		// 'Clear DTMF buffer' flag.
		protected int _clearDTMFBuffer = 0;
		// UserLevel
		protected string _userLevel = "";

		// local rep of data
		protected byte[] _rawData;
		// local pointer to where in buffer we are.
		protected long _currentPos = 0;

		// List of all outputs from this block.
		private ArrayList blockOutputs = new ArrayList();
		// List of all local outputs from this block.
		private ArrayList localBlockOutputs = new ArrayList();
		// List of statistic-records
		private ArrayList statRecords = new ArrayList();
		// Special note for this block
		public Note blockNote = null;

		/// <summary>
		/// Abstract method that returns the type of block.
		/// </summary>
		/// <returns>block type</returns>
		public abstract TYPE GetBlockType();
		
		/// <summary>
		/// Abstract method that returns the XML for this block.
		/// </summary>
		/// <param name="x">x-position in graph-view (display)</param>
		/// <returns>xml-data</returns>
		public abstract string GetXML(int x, int y);

		/// <summary>
		/// Returns the width of the block. Here in the baseclass we return the
		/// default block size, ie the SetVar size.
		/// </summary>
		/// <returns></returns>
		public virtual int GetWidth()
		{
			return 96;
		}


		public virtual void AppendNextData(byte[] rawData)
		{
			Trace.Debug("AppendNextData called on base class for blocktype " + GetBlockType());
		}

		/// <summary>
		/// Main description-string added to each block that goes through
		/// conversion. Normally adds "jada jada, jens rules..."
		/// </summary>
		/// <returns></returns>
		public string GetDescriptionXML()
		{
			return " description=\"" + Utils.XmlString(Settings.Instance.defaultDescription) + "\"";
		}

		public string GetClearDTMFXML()
		{
			return " clearDTMF=\"" + _clearDTMFBuffer + "\"";
		}

		public string GetMinUserLevelXML()
		{
			return " minUserLevel=\"" + Utils.XmlString(_userLevel) + "\"";
		}

		public string GetControlPanelMessageXML()
		{
			return " cpMessage=\"" + Utils.XmlString(_channelDisplayText) + "\"";
		}


		/// <summary>
		/// Called after all data has been read and before XML-getters are called.
		/// Makes the id-stuff.
		/// </summary>
		public void Process()
		{
			IEnumerator enumer = blockOutputs.GetEnumerator();
			enumer.Reset();
			while (enumer.MoveNext())
			{
				BlockOutput bo = (BlockOutput)enumer.Current;
				int id = Settings.Instance.globalIdSeq++;
				bo.SetId(id);
			}
		}

		/// <summary>
		/// Statistics page setup. Gather all stats-entries and make XML
		/// of it. Return string.
		/// </summary>
		/// <returns></returns>
		public string GetLogsXML()
		{
			string ret = "\t\t\t\t<Logs>\r\n";
			IEnumerator enumer = statRecords.GetEnumerator();
			enumer.Reset();
			while (enumer.MoveNext())
			{
				StatisticEntry stat = (StatisticEntry)enumer.Current;
				ret += "\t\t\t\t\t<LogEntry ";
				ret += "criteria= \"" + Utils.XmlString(stat._logCriteria) + "\" ";
				ret += "expression=\"" + Utils.XmlString(stat._expression) + "\" ";
				ret += "description=\"" + Utils.XmlString(stat._description) + "\" ";
				ret += "globalLog=\"" + stat._globalLog + "\" ";
				ret += "afterBefore=\"" + stat._logAfterExecution + "\"";
				ret += "/>\r\n";
			}
			ret += "\t\t\t\t</Logs>\r\n";
			return ret;
		}

		/// <summary>
		/// Not all blocks needs named inputs. They get default "Next"
		/// </summary>
		/// <param name="blockId"></param>
		/// <returns></returns>
		public string GetInputsXML(long blockId)
		{
			return GetInputsXML(blockId, "Next");
		}

		/// <summary>
		/// Create an input with the given name for the "block id"
		/// </summary>
		/// <param name="blockId"></param>
		/// <param name="inputName"></param>
		/// <returns></returns>
		public string GetInputsXML(long blockId, string inputName)
		{
			string ret = "";
			// <Inputs>
			ret += "\t\t\t\t<Inputs>\r\n";
			// <Input _id="BLOCK_ID" name="NAME">
			ret += "\t\t\t\t\t<Input _id=\"" + blockId + "\" name=\"" + inputName + "\"/>\r\n";
			// </Inputs>
			ret += "\t\t\t\t</Inputs>\r\n";
			return ret;
		}

		/// <summary>
		/// Clean name from uniqifiers, such as _, _1, _2, etc...
		/// </summary>
		/// <param name="name"></param>
		/// <returns></returns>
		private string CleanName(string name)
		{
			int pos = name.IndexOf("_");
			if (pos != -1)
			{
				name = name.Remove(pos, name.Length - pos);
				return name;
			}
			return name;
		}


		private int GetHandledErrorId(BlockOutput bo)
		{
			Block.OUTPUT outType = bo.GetOutputType();
			string outputName = outType.ToString();
			
			// walk block's list of supported outputs and see if we can find any code
			// number to use. Matching using name.
			foreach (BlockOutput ibo in outputErrorIds)
			{
				Block.OUTPUT typ = ibo.GetOutputType();
				string name = typ.ToString();
				if (name == outputName)
				{
					return ibo.GetErrorNumber();
				}
			}
			throw (new UnimplementedException("Output '" + outputName +
				"' in block " + this.GetType().ToString() + " have no mapping to " +
				"any blockOutput id, internal error."));
		}


		/// <summary>
		/// For some reason, the block-id that the 'Next', 'Disconnect' are referring to are 1 off, so make it -1. 
		/// </summary>
		/// <returns></returns>
		public string GetOutputsXML()
		{
			// <Outputs>
			string ret = "\t\t\t\t<Outputs>\r\n";
			// Add local outputs
			IEnumerator localEnumer = localBlockOutputs.GetEnumerator();
			localEnumer.Reset();
			while (localEnumer.MoveNext())
			{
				KeyVal kv = (KeyVal)localEnumer.Current;
				string name = kv.GetKey();
				long ptr = Convert.ToInt32(kv.GetValue());
				// <Output name="NAME" ...
				ret += "\t\t\t\t\t<Output name=\"" + name + "\"";
				if (ptr != 0xFFFF)
				{
					// ...input="PTR" _id="GLOBAL_ID" />
					ret += " input=\"" + (ptr-1) + "\"/>\r\n";
				}
				else
				{
					// .../>
					ret += "/>\r\n";
				}
			}

			// Add built-in outputs.
			IEnumerator enumer = blockOutputs.GetEnumerator();
			enumer.Reset();
			while (enumer.MoveNext())
			{
				BlockOutput bo = (BlockOutput)enumer.Current;
				OUTPUT outputType = bo.GetOutputType();
				string name = outputType.ToString();
				// Clean name from _ and _1 or _2's.
				name = CleanName(name);
				long ptr = bo.GetConnectsTo();
				// <Output name="NAME" ...
				ret += "\t\t\t\t\t<Output name=\"" + name + "\"";
				if (ptr != 0xFFFF)
				{
					// ...input="PTR" _id="GLOBAL_ID" />
					ret += " input=\"" + (ptr-1) + "\" _id=\"" + bo.GetId()	+ "\"/>\r\n";
				}
				else
				{
					// .../>
					ret += " _id=\"" + bo.GetId() + "\"/>\r\n";
				}
			}
			// </Outputs>
			ret += "\t\t\t\t</Outputs>\r\n";
			return ret;
		}



		/// <summary>
		/// Difference to GetHandledErrorsXML() is that we have the Errors node to.
		/// </summary>
		/// <returns></returns>
		public string GetHandledErrorsXML_EventCheckSpecial()
		{
			// <HandledErrors>
			string ret = "\t\t\t\t<HandledErrors>\r\n";
			IEnumerator enumer = blockOutputs.GetEnumerator();
			enumer.Reset();
			
			// Here goes the Next output, always defined.
			enumer.MoveNext();
			BlockOutput bo1 = (BlockOutput)enumer.Current;
			ret += "\t\t\t\t\t<HandledError ";
			ret += "index=\"" + GetHandledErrorId(bo1) + "\" ";
			ret += "output=\"" + bo1.GetId() + "\"/>\r\n";			
			ret += "\t\t\t\t</HandledErrors>\r\n";
			
			// Start errors tag
			ret += "\t\t\t\t<Errors>\r\n";
			while (enumer.MoveNext())
			{
				BlockOutput bo = (BlockOutput)enumer.Current;
				try
				{
					int errId = GetHandledErrorId(bo);
					ret += "\t\t\t\t\t<HandledError ";
					ret += "index=\"" + errId + "\" ";
					ret += "output=\"" + bo.GetId() + "\"/>\r\n";
				}
				catch (UnimplementedException)
				{
					AddToBlockNote("This block '" + _name + "' had an unknown ouput " +
						"with name " + bo.GetOutputType() + ". Please check and add this " +
						"output manually.\r\n");
				}
			}
			// </Errors>
			ret += "\t\t\t\t</Errors>\r\n";
			return ret;
		}


		public string GetHandledErrorsXML()
		{
			// <HandledErrors>
			string ret = "\t\t\t\t<HandledErrors>\r\n";
			
			IEnumerator enumer = blockOutputs.GetEnumerator();
			enumer.Reset();
			while (enumer.MoveNext())
			{
				BlockOutput bo = (BlockOutput)enumer.Current;
				try
				{
					int errId = GetHandledErrorId(bo);
					ret += "\t\t\t\t\t<HandledError ";
					ret += "index=\"" + errId + "\" ";
					ret += "output=\"" + bo.GetId() + "\"/>\r\n";
				}
				catch (UnimplementedException)
				{
					AddToBlockNote("This block '" + _name + "' had an unknown ouput " +
						"with name " + bo.GetOutputType() + ". Please check and add this " +
						"output manually.\r\n");
				}
			}
			// </HandledErrors>
			ret += "\t\t\t\t</HandledErrors>\r\n";
			return ret;
		}


		public string GetName()
		{
			return _name;
		}

		public long GetId()
		{
			return _blockId;
		}


		public void AddToBlockNote(string str)
		{
			if (blockNote == null)
			{
				blockNote = new Note("");
			}
			blockNote.Text += str + "\r\n";
		}


		/// <summary>
		/// Add an output to the list of outputs.
		/// </summary>
		/// <param name="output">Name of the output, ie Next, ConfDestroyed, etc</param>
		/// <param name="blockId">Id to which block this output is connected.</param>
		private void AddOutput(OUTPUT output, long blockId)
		{
			Trace.Debug("AddOutput: " + output.ToString() + " -> " + blockId);
			if (!Enum.IsDefined(typeof(OUTPUT), output))
			{
				string hex = System.Convert.ToString((int)output, 16);
				Trace.Info("Output type of block " + _name + " is unknown: 0x" + hex + ". This "+
					"will make the output ens file compilable, but not identical with original.");
				
				AddToBlockNote("This block '" + _name + "' had an unknown ouput with id 0x" + hex + ". Please check and add this output manually.");
				Settings.Instance.unknownOutputsFound = true;
			}
			else
			{
				try
				{
					BlockOutput bo = new BlockOutput(output, blockId, -1);
					blockOutputs.Add(bo);
				}
				catch (UnimplementedException)
				{
					Trace.Info("This will make the output ens file compilable, but not identical with original.");
					AddToBlockNote("This block '" + _name + "' had an unknown ouput with name " + output.ToString() + ". Please check and add this output manually.\r\n");
					Settings.Instance.unknownOutputsFound = true;
				}
			}
		}

		/// <summary>
		/// Add a named output to lists. This will make them appear when
		/// getter for output xml is called.
		/// </summary>
		/// <param name="name"></param>
		/// <param name="blockId"></param>
		protected void AddLocalOutput(string name, long blockId)
		{
			localBlockOutputs.Add(new KeyVal(name, "" +blockId));
		}


		private bool ReadStatistics()
		{
			Trace.Debug("ReadStatistics() start.");
			// Get number of stats fields.
			long statFields = _rawData[32] + _rawData[33] * 256;
			// Read first all the info about the records.
			for (long i=1; i <= statFields; i++)
			{
				int globalLog = _rawData[_currentPos + 12];
				int logAfterExecution = _rawData[_currentPos + 14];
				StatisticEntry entry = new StatisticEntry(globalLog, logAfterExecution);
				statRecords.Add(entry);
				_currentPos += 16;
			}
			for (long i=1; i <= statFields; i++)
			{
				StatisticEntry entry = (StatisticEntry)statRecords[(int)i-1];
				// 
				string expression = Utils.DataToString(_rawData, _currentPos);
				_currentPos = _currentPos + Utils.DataStringLength(_rawData, _currentPos);

				string description = Utils.DataToString(_rawData, _currentPos);
				_currentPos = _currentPos + Utils.DataStringLength(_rawData, _currentPos);

				string logCriteria = Utils.DataToString(_rawData, _currentPos);
				_currentPos = _currentPos + Utils.DataStringLength(_rawData, _currentPos);

				entry._expression = expression;
				entry._description = description;
				entry._logCriteria = logCriteria;
				entry.Print();
			}
			Trace.Debug("Read " + statFields + " statistic-fields.");
			return true;
		}


		/// <summary>
		/// Given the number of block-outputs to read, this method reads them
		/// and adds them to the output list.
		/// This is called for each block through ReadOutputs() method, but also
		/// from the eventcheck that has special outputs.
		/// </summary>
		/// <param name="numberOfOutputsToRead"></param>
		/// <returns></returns>
		protected bool ReadOutputs(int numberOfOutputsToRead)
		{
			for (int i=1; i <= numberOfOutputsToRead; i++)
			{
				Trace.Debug("ReadOutputs " + i + " of " + numberOfOutputsToRead);
				long outputType = _rawData[_currentPos] + _rawData[_currentPos+1] * 256;
				long blockId = _rawData[_currentPos+2] + _rawData[_currentPos+3] * 256;
				OUTPUT outType = (OUTPUT)outputType;
				AddOutput(outType, blockId);
				
				// Jump forward.
				_currentPos += 4;
			}
			return true;
		}


		/// <summary>
		/// Method called for all block constructions. Reads the number of
		/// block-outputs from the block-header and calls ReadOutputs()
		/// that reads the actual outputs.
		/// </summary>
		/// <returns>success or failure</returns>
		private bool ReadOutputs()
		{
			int numberOfOutputs = _rawData[34];
			
			// TODO: Somewhere here we have a MAJOR problem. When working with Stop-
			// blocks, then the number of outputs are in another location (or just
			// are not written.) Normally we can read ONE output here on that block-
			// type, but since we don't want any anyways, we just skip it for now.
			// This is a real fixme!
			TYPE type = (Block.TYPE)_rawData[30];
			if (type == TYPE.Stop)
			{
				numberOfOutputs = 0;
			}
			return ReadOutputs(numberOfOutputs);
		}

		/// <summary>
		/// Some block-outputs have different names in Studio, but are essentially
		/// the same in .seq format. To reverse, these needs to be changed.
		/// See TimeSwitch block where 'Next' in .seq becomes 'Working' in 
		/// reversed format.
		/// </summary>
		/// <param name="from"></param>
		/// <param name="to"></param>
		protected void ChangeOutputType(OUTPUT from, OUTPUT to)
		{
			IEnumerator enumer = blockOutputs.GetEnumerator();
			enumer.Reset();
			while (enumer.MoveNext())
			{
				BlockOutput bo = (BlockOutput)enumer.Current;
				if (bo.GetOutputType() == from)
				{
					Trace.Debug("Changing output type from " + bo.GetOutputType() + " to " + 
						to.ToString());
					bo.SetOutputType(to);
				}
			}
		}


		private bool ReadUserLevel()
		{
			Trace.Debug("ReadUserLevel start");
			if (_rawData[8] != 0)
			{
				_userLevel = Utils.DataToString(_rawData, _currentPos);
				_currentPos = _currentPos + Utils.DataStringLength(_rawData, _currentPos);
				Trace.Debug("Userlevel: " + _userLevel);			
			}
			else
			{
				Trace.Debug("Userlevel is not enabled.");			
			}
			return true;
		}



		public Block(byte[] rawData)
		{
			// Global events
			outputErrorIds.Add(new BlockOutput(OUTPUT.ConfDestroyed, 127));
			outputErrorIds.Add(new BlockOutput(OUTPUT.CreditOut, 17));
			outputErrorIds.Add(new BlockOutput(OUTPUT.DDEReceived, 70));
			outputErrorIds.Add(new BlockOutput(OUTPUT.Disconnect, 15));
			outputErrorIds.Add(new BlockOutput(OUTPUT.DiskFull, 67));
			outputErrorIds.Add(new BlockOutput(OUTPUT.Exit, 18));
			outputErrorIds.Add(new BlockOutput(OUTPUT.ExprVal, 28));
			outputErrorIds.Add(new BlockOutput(OUTPUT.FaxRecvTone, 72));
			outputErrorIds.Add(new BlockOutput(OUTPUT.FaxSendTone, 71));
			outputErrorIds.Add(new BlockOutput(OUTPUT.HookFlash, 126));
			outputErrorIds.Add(new BlockOutput(OUTPUT.IEReceived, 157));
			outputErrorIds.Add(new BlockOutput(OUTPUT.ModeChange, 19));
			outputErrorIds.Add(new BlockOutput(OUTPUT.ModemTone, 84));
			outputErrorIds.Add(new BlockOutput(OUTPUT.Notify, 98));
			outputErrorIds.Add(new BlockOutput(OUTPUT.OpBreak, 16));
			outputErrorIds.Add(new BlockOutput(OUTPUT.Restart, 20));
			outputErrorIds.Add(new BlockOutput(OUTPUT.Ring, 125));
			outputErrorIds.Add(new BlockOutput(OUTPUT.SetVariable, 29));
			outputErrorIds.Add(new BlockOutput(OUTPUT.UserLevel, 68));
			outputErrorIds.Add(new BlockOutput(OUTPUT.UUIReceived, 156));
			
			Trace.Debug("Block()");
			_rawData = rawData;

			// Get block id number
			_blockId = _rawData[28] + _rawData[29] * 256;
			_blockId--;

			// clear DTMF-buffer is a globala param, even if settable for only some blocks...
			_clearDTMFBuffer = _rawData[38];

			// Get null-terminated name from pos 40
			_name = Utils.DataToString(rawData, 40);
			// Move pointer just past the name
			_currentPos = 40 + Utils.DataStringLength(rawData, 40);
		
			//User-level
			ReadUserLevel();

			// Read all statistics fields
			ReadStatistics();

			// read outputs
			ReadOutputs();

			// Read the channel display text
			_channelDisplayText = Utils.DataToString(rawData, _currentPos);
			// Move pointer just past the channel display text
			_currentPos = _currentPos + Utils.DataStringLength(rawData, _currentPos);

			Trace.Debug("NAME = " + _name + ", id="+_blockId+", DisplayText = " + _channelDisplayText);
		}

	}
}
